use crate::client::*;
use away_mq_core::{message::*, router::*};
use socket2::{Domain, Socket, Type};
use std::{
    io::Read,
    net::{SocketAddr, TcpListener},
};
use tokio::spawn;

#[derive(Default)]
pub struct Server {
    router: Router,
    client_manager: Client,
}

impl Server {
    pub fn new() -> Self {
        Self::default()
    }
    pub async fn listen(&mut self, addr: &str) {
        let socket: Socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap();
        socket
            .bind(&addr.parse::<SocketAddr>().unwrap().into())
            .unwrap();
        socket.listen(128).unwrap();
        let listener: TcpListener = socket.into();

        loop {
            let (socket, addr) = listener.accept().unwrap();
            // auth
            // add client
            self.client_manager
                .set_client(addr.to_string().as_str(), socket.try_clone().unwrap());

            // reader
            let mut r = socket.try_clone().unwrap();
            let mut client_manager = self.client_manager.clone();
            let mut router = self.router.clone();
            spawn(async move {
                let mut buf = [0; 1024 * 10];
                loop {
                    let len = match r.read(&mut buf) {
                        Ok(len) => len,
                        Err(_) => {
                            client_manager.remove_client(addr.to_string().as_str());
                            break;
                        }
                    };

                    if len == 0 {
                        continue;
                    }

                    let payload = PayloadMessage::deserialize(&buf[0..len]).unwrap();
                    client_manager.send_all(&payload);
                    router.run(&payload);
                }
            });
        }
    }

    pub fn publish(&mut self, id: &str, payload: PayloadMessage) {
        self.client_manager.send(id, payload);
    }

    pub fn subscribe(&mut self, method: &str, handle: &'static RouterHandle) {
        self.router.add_router(method, handle);
    }
}
